/**
    Copyright (c) 2010 yakiimo02
    Distributed under the New BSD License.
    See included license.txt or http://www.yakiimo3d.com/NewBSDLicense.txt
**/

#include "DXUT.h"
#include "BuddhabrotMgr.h"

BuddhabrotMgr    g_buddhabrotMgr;

/**
*/
BuddhabrotMgr::BuddhabrotMgr() : m_mode( BUDDHABROT_MODE_CS )
{
}

/**
*/
BuddhabrotMgr::~BuddhabrotMgr()
{
}

/**
*/
HRESULT BuddhabrotMgr::OnCreateDevice( ID3D11Device* pd3dDevice )
{
    HRESULT hr = S_OK;
    V_RETURN( m_cs.OnCreateDevice( pd3dDevice ) );
    return S_OK;
}

/**
*/
HRESULT BuddhabrotMgr::OnResizedSwapChain( ID3D11Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc )
{
    HRESULT hr = S_OK;

    // recreate buddabrot databuffer to match screen back buffer dimensions
    V_RETURN( m_dataBufferDX11.OnResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc ) );

    V_RETURN( m_cs.OnResizedSwapChain( pd3dDevice, &m_dataBufferDX11 ) );
    
    m_cpu.Create( &m_dataBufferDX11 );

    return S_OK;
}

/**
*/
void BuddhabrotMgr::OnFrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext,
                      double fTime, float fElapsedTime, DataBufferDX11* pDataBuffer )
{
    if( m_mode == BUDDHABROT_MODE_CPU ) {
        m_cpu.Render( pd3dDevice, pd3dImmediateContext, &m_dataBufferDX11 );
    }
    else if( m_mode == BUDDHABROT_MODE_CS ) {
        m_cs.OnFrameRender( pd3dDevice, pd3dImmediateContext, fTime, fElapsedTime, pDataBuffer );
    }    
}

/**
*/
void BuddhabrotMgr::OnDestroyDevice()
{
    m_cs.OnDestroyDevice();
    m_cpu.Destroy();
    m_dataBufferDX11.Destroy();
}

/**
*/
void BuddhabrotMgr::TranslateCenter( const D3DXVECTOR2& vDeltaCenter )
{
    m_cs.TranslateCenter( vDeltaCenter );
    m_cpu.TranslateCenter( vDeltaCenter );
    m_cs.SetbClearOutputUAV( true );
}

/**
*/
void BuddhabrotMgr::ZoomScale( const float fDeltaScale )
{
    m_cs.ZoomScale( fDeltaScale );
    m_cpu.ZoomScale( fDeltaScale );
    m_cs.SetbClearOutputUAV( true );
}

void BuddhabrotMgr::SetnMaxIterations( const int nRGB, const int nMaxIterations )
{
    m_cs.SetnMaxIterations( nRGB, nMaxIterations );
    m_cpu.SetnMaxIterations( nRGB, nMaxIterations );
}

/**
*/
LRESULT BuddhabrotMgr::HandleMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    // Current mouse position
    int nMouseX = ( short )LOWORD( lParam );
    int nMouseY = ( short )HIWORD( lParam );
    D3DXVECTOR2 vPosNow = D3DXVECTOR2( static_cast< float >( nMouseX ), static_cast< float >( nMouseY ) );

    static D3DXVECTOR2 vPosPrevL;
    static D3DXVECTOR2 vPosPrevR;

    //float fFPS = DXUTGetFPS();
    //if( fFPS < 1.0f ) { fFPS = 60.0f; }
    float fFPS = 60.0f;

    bool bCtrlKeyDown = false;
    if( GetAsyncKeyState( VK_LCONTROL ) & 0x8000 ) {
        bCtrlKeyDown = true;
    }

    switch( uMsg )
    {
        case WM_LBUTTONDOWN:
        case WM_LBUTTONDBLCLK:
            vPosPrevL = vPosNow;
            return TRUE;

        case WM_LBUTTONUP:
            return TRUE;

        case WM_RBUTTONDOWN:
        case WM_RBUTTONDBLCLK:
            vPosPrevR = vPosNow;
            return TRUE;

        case WM_RBUTTONUP:
        case WM_MBUTTONUP:
            return TRUE;

        case WM_MOUSEMOVE:
            // check whether ctrl key is being held down.
            if( bCtrlKeyDown )
            {
                if( ( MK_LBUTTON & wParam ) )
                {
                    m_cpu.ClearIterations();
                    const float POS_DELTA = 0.005f;
                    D3DXVECTOR2 vPosDiff = vPosNow - vPosPrevL;
                    D3DXVECTOR2 vPosDiffScaled = vPosDiff * POS_DELTA * ( 60.0f / fFPS );
                    vPosDiffScaled.x = -vPosDiffScaled.x;
                    TranslateCenter( vPosDiffScaled );
                    vPosPrevL = vPosNow;
                }
                if( ( MK_RBUTTON & wParam ) )
                {
                    m_cpu.ClearIterations();
                    const float ZOOM_DELTA = 0.01f;
                    float fPosDiff = ( vPosNow.x - vPosPrevR.x ) * ZOOM_DELTA;
                    float fPosDiffScaled = 1.0f - fPosDiff * ( 60.0f / fFPS );
                    if( fPosDiffScaled < 0.0f ) {
                        fPosDiffScaled  = 1.0f / abs( fPosDiffScaled );
                    }
                    ZoomScale( fPosDiffScaled );
                    vPosPrevR = vPosNow;                
                }
            }
            return TRUE;
    }

    return FALSE;    
}